﻿#region using statements

using System;

using Microsoft.Xna.Framework;

using NGen;
using NGen.Managers.GameObject;
using NGen.Systems.StringAlarms;

using TowerDefense.Misc;

#endregion

namespace TowerDefense.Objects.Enemies
{
	/// <summary>
	/// 	Base class for an enemy object
	/// </summary>
	public class GameObjectEnemyBase : GameObject2D, IAlarmListener, IComparable<GameObjectEnemyBase>
	{
		#region local fields

		/// <summary>
		/// 	the path the enemies will follow
		/// </summary>
		public static Point[] m_lWaypoint;

		/// <summary>
		/// 	Alarm system which manages the alarms for this enemy
		/// </summary>
		protected AlarmSystem m_alarm = new AlarmSystem();

		/// <summary>
		/// 	alarm which governs the fire burn damage
		/// </summary>
		protected AlarmObject m_fireAlarm;

		/// <summary>
		/// 	alarm which keeps track of the length of the slow effect
		/// </summary>
		protected AlarmObject m_iceAlarm;

		/// <summary>
		/// 	currnet node in the waypoint path that this enemy is currently
		/// 	seeking
		/// </summary>
		private int m_waypointTargetIndex;

		#endregion

		#region local properties

		/// <summary>
		/// 	Get the amount of gold this enemy is worth
		/// </summary>
		public int Bounty { get; protected set; }

		/// <summary>
		/// 	Gets the amount of fire damage to be dealt the next second
		/// </summary>
		public int FireDamageNextSecond { get; protected set; }

		/// <summary>
		/// 	Get the magnitude of the slow effect on this enemy
		/// </summary>
		public float SlowedPercentage { get; protected set; }

		/// <summary>
		/// 	Get movement speed of this enemy
		/// </summary>
		public float Velocity { get; protected set; }

		/// <summary>
		/// 	Get the remaining health of this enemy
		/// </summary>
		public int Health { get; set; }

		#endregion

		#region local methods

		/// <summary>
		/// 	Apply fire damage DoT effect to this enemy. Updates the damage
		/// 	amount and makes the alarm active if it is inactive
		/// </summary>
		/// <param name = "p_damage">Amount of damage to do via burn</param>
		public void ApplyFire(int p_damage) {
			//if alarm is not active
			if(m_fireAlarm.TimeRemaning <= 0.0) {
				//set it active
				m_fireAlarm.TimeRemaning = 1;
				m_alarm.Add(m_fireAlarm);
			}

			//if this new burn amount is greater than any preexisting
			//burn effect, use this value instead
			if(p_damage > FireDamageNextSecond) {
				FireDamageNextSecond = p_damage;
			}
		}

		/// <summary>
		/// 	Apply the slow effect from an ice tower hit
		/// </summary>
		/// <param name = "p_slowPercent">The percentage of slow to apply to this
		/// 	enemy</param>
		public void ApplyIce(float p_slowPercent) {
			//if ice alarm is not active, set it active
			if(m_iceAlarm.TimeRemaning <= 0.0) {
				m_alarm.Add(m_iceAlarm);
			}

			//reset the duration of slow upon hit
			m_iceAlarm.TimeRemaning = 2;

			//increase the slow amount if necessary
			if(SlowedPercentage < p_slowPercent) {
				SlowedPercentage = p_slowPercent;
			}
		}

		#endregion

		#region GameObject2D implementation

		/// <summary>
		/// 	_Initialize this enemy
		/// </summary>
		protected override void _Initialize() {
			if(m_texture != null) {
				m_textureOffset.X = m_texture.Height / 2.0f;
				m_textureOffset.Y = m_texture.Width / 2.0f;
			}
			m_fireAlarm = new AlarmObject(this, 0.0, "fire");
			m_iceAlarm = new AlarmObject(this, 0.0, "ice");
		}

		/// <summary>
		/// 	Update this enemy for this frame
		/// </summary>
		/// <param name = "p_gameTime"></param>
		protected override void _Update(GameTime p_gameTime) {
			//update alarms
			m_alarm.Update(p_gameTime);

			//destroy this object if it is low on health
			if(Health <= 0) {
				GameEngine.GetService<GameStats>().Gold += Bounty;
				GameEngine.GetService<GameStats>().TotalGold += Bounty;
				Destroy(Id);
			}

			//if close to the current waypoint, move to the next one
			if(Vector2.DistanceSquared(m_position, GameLevel.GetScreenCoords(m_lWaypoint[m_waypointTargetIndex])) <=
			   Velocity * p_gameTime.ElapsedGameTime.TotalSeconds) {
				m_waypointTargetIndex++;

				//if the end of the path is reached
				if(m_waypointTargetIndex == m_lWaypoint.Length) {
					Destroy(Id); //destroy this object

					//take a life
					GameEngine.GetService<GameStats>().Lives -= 1;
					return;
				}
			}

			//calculate direction to move towards next waypoint
			var offset = Vector2.Subtract(GameLevel.GetScreenCoords(m_lWaypoint[m_waypointTargetIndex]), m_position);

			//normalize this direction and multiple by velocity, taking into account slow effect
			offset.Normalize();
			m_position += offset * Velocity * ((1.0f - SlowedPercentage) * (float) p_gameTime.ElapsedGameTime.TotalSeconds);
		}

		#endregion

		#region IComparable implementation

		/// <summary>
		/// 	When two enemies are compared, the "greater" enemy is the one which
		/// 	is furthest along the path
		/// </summary>
		/// <param name = "p_other">the other object to be used in comparing</param>
		public int CompareTo(GameObjectEnemyBase p_other) {
			return -1 * m_waypointTargetIndex.CompareTo(p_other.m_waypointTargetIndex);
		}

		#endregion

		#region IAlarmListener implementation

		/// <summary>
		/// 	Called by the alarm systems whenever an alarm goes off
		/// </summary>
		/// <param name = "p_message">the message carried by the alarm</param>
		public void OnAlarmEvent(string p_message) {
			switch(p_message) {
				case "ice":
					SlowedPercentage = 0;
					break;
				case "fire":
					//apply fire damage
					Health -= FireDamageNextSecond;

					//half the fire damage for the next second
					FireDamageNextSecond /= 2;

					//if there is any fire damage left
					if(FireDamageNextSecond > 0) {
						//reset the alarm again
						m_fireAlarm.TimeRemaning = 1;
						m_alarm.Add(m_fireAlarm);
					}
					break;
			}
		}

		#endregion
	}
}